﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Windows.Data;

namespace Integrate.Window.UI.Converter
{
    /// <summary>
    /// 将绑定的值根据传入的表达式进行计算
    /// </summary>
    
    [ValueConversion(typeof(double), typeof(double))]
    public class MathCovertor : IValueConverter
    {
        //private static readonly char[] _allOperators = new[] { '(', ')', '+', '-', '*', '/', '%' };
        private static readonly char[] _allOperators = new[] { ')','(','-','+','%','/','*'};


        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            //format parameter
            string mathExpressionStr = parameter as string;
            mathExpressionStr=mathExpressionStr.Replace(" ", "");
            mathExpressionStr=mathExpressionStr.Replace("@Value",value.ToString());

            var numberstrs = mathExpressionStr.Split(_allOperators, StringSplitOptions.RemoveEmptyEntries);

            var numbers = new List<double>();

            foreach (var tempStr in numberstrs)
            {
                double temValue;
                if(double.TryParse(tempStr, out temValue))
                {
                    numbers.Add(temValue);
                }
                else
                {
                    throw new FormatException($"{tempStr} con't parse to double!");
                }
            }

            return CalcExpression(numbers, mathExpressionStr);
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }

        static double CalcExpression(List<double> numbers, string expression)
        {
            var numerStack = new Stack<double>();
            var operStack = new Stack<char>();

            var numberIndex = 0;
            for (int i = 0; i < expression.Length;)
            {
                int index = expression.IndexOfAny(_allOperators, i);

                if (index == i)
                {
                    char ch = expression[index];
                    if (ch == '(')
                    {
                        operStack.Push(ch);
                    }
                    //pop until pop '('
                    else if (ch == ')')
                    {
                        while (operStack.Peek() != '(')
                        {
                            CalcSubOperation(operStack, numerStack);
                        }
                        operStack.Pop();  //Pop '('
                    }
                    else
                    {
                        if (operStack.Count > 0 && (GetPriorityLevel(ch) < GetPriorityLevel(operStack.Peek())))
                        {
                            CalcSubOperation(operStack, numerStack);
                        }

                        operStack.Push(ch);
                    }

                    i++;
                }
                else if (index > 0)
                {
                    numerStack.Push(numbers[numberIndex]);
                    numberIndex++;
                    i = index;
                }
                else
                {
                    if (i < expression.Length)
                    {
                        numerStack.Push(numbers[numberIndex]);
                        numberIndex++;
                        i++;
                    }
                }
            }

            while (operStack.Count > 0)
            {
                CalcSubOperation(operStack, numerStack);
            }

            if (numerStack.Count == 1)
            {
                return numerStack.Pop();
            }

            return 0;
        }

        static int GetPriorityLevel(char symbol)
        {
            switch (symbol)
            {
                case '-':
                case '+':
                    return 0;
                case '*':
                case '/':
                case '%':
                    return 1;
                default:
                    return -1;
            }
        }

        static void CalcSubOperation(Stack<char> operStack, Stack<double> numberStack)
        {
            double rightNumber = numberStack.Pop();
            double leftNumber = numberStack.Pop();

            switch (operStack.Pop())
            {
                case '+':
                    numberStack.Push(leftNumber + rightNumber);
                    break;
                case '-':
                    numberStack.Push(leftNumber - rightNumber);
                    break;
                case '*':
                    numberStack.Push(leftNumber * rightNumber);
                    break;
                case '/':
                    numberStack.Push(leftNumber / rightNumber);
                    break;
                case '%':
                    numberStack.Push(leftNumber % rightNumber);
                    break;
                default:
                    break;
            }
        }
    }
}
